package org.base.sys.service.impl;

import lombok.AllArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.lang3.StringUtils;
import org.base.ConstantPool;
import org.base.common.exception.BusinessException;
import org.base.common.exception.ExceptionEnum;
import org.base.common.service.impl.ServiceImpl;
import org.base.common.util.ObjectCopyUtils;
import org.base.common.util.TreeUtils;
import org.base.sys.VO.MenuMetaVO;
import org.base.sys.VO.MenuVO;
import org.base.sys.VO.SysMenuPermissionVO;
import org.base.sys.VO.SysRoleVO;
import org.base.sys.dao.SysRoleMapper;
import org.base.sys.pojo.SysMenuPermission;
import org.base.sys.pojo.SysRole;
import org.base.sys.query.SysRoleQuery;
import org.base.sys.service.SysMenuPermissionService;
import org.base.sys.service.SysRoleMenuPermissionService;
import org.base.sys.service.SysRoleService;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.io.Serializable;
import java.util.*;
import java.util.stream.Collectors;

/**
 * <p>
 * 角色信息 服务实现类
 * </p>
 *
 * @author AutoGenerator
 * @since 2020-05-27
 */
@Service
@Slf4j
@AllArgsConstructor
public class SysRoleServiceImpl extends ServiceImpl<SysRoleMapper, SysRole, SysRoleQuery> implements SysRoleService {


    private final SysRoleMenuPermissionService roleMenuPermissionService;

    private SysMenuPermissionService menuPermissionService;

    @Override
    @Transactional
    public boolean saveOrUpdate(SysRole entity) {
        if (entity.getId() == null) {
            return super.saveOrUpdate(entity);
        } else {
            return lambdaUpdate().set(SysRole::getName, entity.getName())
                    .set(SysRole::getSeq, entity.getSeq())
                    .set(SysRole::getStatus, entity.getStatus())
                    .eq(SysRole::getId, entity.getId())
                    .update();
        }
    }

    @Override
    public boolean disable(Long id) {
        return switchStatus(id, 1);
    }

    @Override
    public boolean enable(Long id) {
        return switchStatus(id, 0);
    }

    private boolean switchStatus(Long id, int status) {
        return lambdaUpdate()
                .set(SysRole::getStatus, status)
                .eq(SysRole::getId, id)
                .update();
    }

    @Override
    public SysRoleVO getRoleVOById(Long id) {
        final SysRole role = getById(id);
        if (role != null) {
            final SysRoleVO roleVO = ObjectCopyUtils.copy(role, SysRoleVO.class);
            List<Long> menuPermissionIds = roleMenuPermissionService.getRoleMenuPermissionIds(roleVO.getId());
            List<Long> checkedMenuPermissionIds = getCheckedMenuPermissionIds(menuPermissionIds);
            roleVO.setMenuPermissionIds(menuPermissionIds);
            roleVO.setCheckedPermissionIds(checkedMenuPermissionIds);
            return roleVO;
        }
        return null;
    }

    private List<Long> getCheckedMenuPermissionIds(List<Long> menuPermissionIds) {

        final List<SysMenuPermissionVO> treeMenuPermission = menuPermissionService.getTreeMenuPermission();
        if (CollectionUtils.isNotEmpty(menuPermissionIds) && CollectionUtils.isNotEmpty(treeMenuPermission)) {
            return getCheckedMenuPermissionIds(menuPermissionIds, treeMenuPermission);
        }
        return Collections.emptyList();
    }

    private List<Long> getCheckedMenuPermissionIds(List<Long> menuPermissionIds, List<SysMenuPermissionVO> treeMenuPermission) {
        List<Long> checkedMenuPermissionIds = new ArrayList<>();
        if (CollectionUtils.isNotEmpty(treeMenuPermission)) {
            for (SysMenuPermissionVO sysMenuPermissionVO : treeMenuPermission) {
                final List<SysMenuPermissionVO> children = sysMenuPermissionVO.getChildren();
                if (menuPermissionIds.contains(sysMenuPermissionVO.getId()) && CollectionUtils.isEmpty(children)) {
                    checkedMenuPermissionIds.add(sysMenuPermissionVO.getId());
                } else {
                    checkedMenuPermissionIds.addAll(getCheckedMenuPermissionIds(menuPermissionIds, children));
                }
            }
        }
        return checkedMenuPermissionIds;
    }

    @Override
    @Transactional
    public boolean saveOrUpdateRoleVO(SysRoleVO entity) {
        SysRole sysRole = ObjectCopyUtils.copy(entity, SysRole.class);
        saveOrUpdate(sysRole);
        sysRole = getById(sysRole.getId());
        roleMenuPermissionService.updateRoleMenuPermissions(sysRole.getId(), entity.getMenuPermissionIds());
        return true;
    }

    @Override
    public List<SysRole> getRoleByIds(List<Long> roleIds) {
        return lambdaQuery().in(SysRole::getId, roleIds).list();
    }

    @Override
    public List<String> getRolePermissions(List<Long> roleIds) {
        return menuPermissionService.getRolePermissions(roleIds);
    }

    @Override
    public List<SysMenuPermission> getRolePerms(List<Long> roleIds) {
        return menuPermissionService.getRolePerms(roleIds);
    }

    @Override
    public List<SysMenuPermission> getAllPerms() {
        return menuPermissionService.getAllPerms();
    }

    @Override
    public List<MenuVO> getRoleMenuByIds(List<Long> roleIds) {
        final List<SysMenuPermission> menus = getRoleVisibleMenus(roleIds);
        if (CollectionUtils.isNotEmpty(menus)) {
            final List<MenuVO> menuVOS = ObjectCopyUtils.copy(menus, MenuVO.class, v -> {
            });
            List<MenuVO> treeMenus = TreeUtils.newTree(menuVOS, Long.valueOf(0), MenuVO::getParentId, MenuVO::getId, MenuVO::setChildren);
            MenuMetaVO meta;
            for (MenuVO menuVO : menuVOS) {
                meta = new MenuMetaVO();
                meta.setTitle(menuVO.getName());
                meta.setIcon(menuVO.getIcon());
                menuVO.setMeta(meta);
            }
            return treeMenus;
        }
        return Collections.EMPTY_LIST;
    }


    @Override
    public List<SysMenuPermission> getRoleVisibleMenus(List<Long> roleIds) {
        if (CollectionUtils.isNotEmpty(roleIds)) {
            List<Long> menuIds = roleMenuPermissionService.getRolesMenuPermissionIds(roleIds);
            return menuPermissionService.getVisibleMenuByIds(menuIds);
        }
        return Collections.emptyList();
    }

    @Override
    public List<SysMenuPermission> getVisibleMenus() {
        return menuPermissionService.getVisibleMenus();
    }

    @Override
    public List<MenuVO> getMenus() {
        final List<SysMenuPermission> menus = getVisibleMenus();
        if (CollectionUtils.isNotEmpty(menus)) {
            final List<MenuVO> menuVOS = ObjectCopyUtils.copy(menus, MenuVO.class, v -> {
            });
            List<MenuVO> treeMenus = TreeUtils.newTree(menuVOS, Long.valueOf(0), MenuVO::getParentId, MenuVO::getId, MenuVO::setChildren);
            MenuMetaVO meta;
            for (MenuVO menuVO : menuVOS) {
                meta = new MenuMetaVO();
                meta.setTitle(menuVO.getName());
                meta.setIcon(menuVO.getIcon());
                menuVO.setMeta(meta);
            }
            return treeMenus;
        }
        return Collections.EMPTY_LIST;
    }

    @Override
    public List<MenuVO> getRoleMenus(List<SysRole> userRoles) {
        if (CollectionUtils.isNotEmpty(userRoles)) {
            boolean isAdmin = false;
            if (CollectionUtils.isNotEmpty(userRoles)) {
                for (SysRole userRole : userRoles) {
                    if (userRole.getCode() != null && userRole.getCode().equals(ConstantPool.ADMIN_CODE)) {
                        isAdmin = true;
                        break;
                    }
                }
            }
            List<SysMenuPermission> menus;
            List<SysMenuPermission> perms;
            final List<Long> roleIds = userRoles.stream().map(SysRole::getId).collect(Collectors.toList());
            // 系统管理员返回所有
            if (isAdmin) {
                menus = getVisibleMenus();
                perms = getAllPerms();
            } else {
                menus = getRoleVisibleMenuPerms(roleIds);
                final List<SysMenuPermission> visibleMenus = getVisibleMenus();
                final List<SysMenuPermission> treeNodes = TreeUtils.getDeduplicationTreeList(visibleMenus, menus, Long.valueOf(0L), SysMenuPermission::getParentId, SysMenuPermission::getId);
                menus = CollectionUtils.isNotEmpty(treeNodes) ? treeNodes.stream().filter(e -> !e.getType().equals(ConstantPool.BUTTON)).collect(Collectors.toList()) : Collections.EMPTY_LIST;
                perms = getRolePerms(roleIds);
            }
            if (CollectionUtils.isNotEmpty(menus)) {
                Collections.sort(menus, Comparator.comparing(SysMenuPermission::getSeq));
            }
            final List<MenuVO> menuVOS = ObjectCopyUtils.copy(menus, MenuVO.class);
            List<MenuVO> treeMenus = TreeUtils.newTree(menuVOS, Long.valueOf(0), MenuVO::getParentId, MenuVO::getId, MenuVO::setChildren);
            MenuMetaVO meta;
            for (MenuVO menuVO : menuVOS) {
                meta = new MenuMetaVO();
                meta.setTitle(menuVO.getName());
                meta.setIcon(menuVO.getIcon());
                menuVO.setMeta(meta);
            }
            if (CollectionUtils.isNotEmpty(perms)) {
                final Map<Long, List<SysMenuPermission>> permGroup = perms.stream().collect(Collectors.groupingBy(SysMenuPermission::getParentId));
                for (MenuVO menuVO : menuVOS) {
                    final Long menuVOId = menuVO.getId();
                    final List<SysMenuPermission> menuPerms = permGroup.get(menuVOId);
                    if (CollectionUtils.isNotEmpty(menuPerms)) {
                        final List<String> permKeys = menuPerms.stream().map(SysMenuPermission::getPermKey).collect(Collectors.toList());
                        menuVO.getMeta().setBtnPerms(permKeys);
                    }
                }
            }
            return treeMenus;
        }
        return null;
    }

    private List<SysMenuPermission> getRoleVisibleMenuPerms(List<Long> roleIds) {
        if (CollectionUtils.isNotEmpty(roleIds)) {
            List<Long> menuIds = roleMenuPermissionService.getRolesMenuPermissionIds(roleIds);
            return menuPermissionService.getVisibleMenuPermByIds(menuIds);
        }
        return Collections.emptyList();
    }

    @Override
    @Transactional
    public boolean removeById(Serializable id) {
        final SysRole role = getById(id);
        if (role.getIsSys() == 1) {
            BusinessException.throwEx(ExceptionEnum.DELETE_ROLE_ERROR);
        }
        return super.removeById(id);
    }
}
